<template>
  <f-popup class="date-picker" :show-flag="showFlag" @update:showFlag="hidePopup" position="bottom">
    <div class="opt-box">
      <f-button size="mini" type="text" color="gray" @click="hidePopup">取消</f-button>
      <f-button size="mini" type="text" @click="handleSubmit">确定</f-button>
    </div>
    <div class="current-time" v-if="range">
        <span
          class="current-time-item"
          :class="activeTime==='startTime'?'active':''"
          @click="handleChangeActiveTime('startTime')">
          {{ formatDate(startTime) }}
        </span>
      <span>至</span>
      <span
        class="current-time-item"
        :class="activeTime==='endTime'?'active':''"
        @click="handleChangeActiveTime('endTime')">
          {{ formatDate(endTime) }}
        </span>
    </div>
    <div class="current-time" v-else>
      <span class="current-time-item active">{{ formatDate(startTime) }}</span>
    </div>
    <f-scroll-box>
      <f-scroll-list ref="year" :data-list="yearRange" :suffix="getSuffix('年')" v-model="time.year"
                     v-if="isRequire.year"></f-scroll-list>
      <f-scroll-list ref="month" :data-list="monthRange" :suffix="getSuffix('月')" v-model="time.month"
                     v-if="isRequire.month"></f-scroll-list>
      <f-scroll-list ref="date" :data-list="dateRange" :suffix="getSuffix('日')" v-model="time.date"
                     v-if="isRequire.date"></f-scroll-list>
      <f-scroll-list ref="hour" :data-list="hourRange" :suffix="getSuffix('时')" v-model="time.hour"
                     v-if="isRequire.hour"></f-scroll-list>
      <f-scroll-list ref="minute" :data-list="minuteRange" :suffix="getSuffix('分')" v-model="time.minute"
                     v-if="isRequire.minute"></f-scroll-list>
      <f-scroll-list ref="second" :data-list="secondRange" :suffix="getSuffix('秒')" v-model="time.second"
                     v-if="isRequire.second"></f-scroll-list>
    </f-scroll-box>
  </f-popup>
</template>

<script>

import FScrollBox from '../scroll/scroll-box';
import FScrollList from '../scroll/scroll-list';
import 'js-utils/datePrototype';
import {Validate} from 'js-utils/validate';
import {toCapitalize} from 'js-utils';

export default {
  name: 'f-date-picker',
  components: {FScrollList, FScrollBox},
  props: {
    //可选值：date,year-month,month-date,datetime,time,hour-minute,minute-second
    type: {
      type: String,
      default: 'date',
      validator(value) {
        const timeTypes = ['date', 'year-month', 'month-date', 'datetime', 'time', 'hour-minute', 'minute-second'];
        return timeTypes.indexOf(value) !== -1;
      }
    },
    format: {
      type: String
    },
    maxTime: {
      type: [Date, String],
      default: () => {
        let date = new Date();
        date.setFullYear(date.getFullYear() + 10);
        return date;
      }
    },
    minTime: {
      type: [Date, String],
      default: () => {
        let date = new Date();
        date.setFullYear(date.getFullYear() - 10);
        return date;
      }
    },
    value: {
      type: [Date, String, Array],
      default: () => new Date()
    },
    placeholder: {
      type: String
    },
    showFlag: {
      type: Boolean,
      default: false
    },
    range: {
      type: Boolean,
      default: false
    },
    yearStep: {
      type: Number,
      default: 1
    },
    monthStep: {
      type: Number,
      default: 1
    },
    dateStep: {
      type: Number,
      default: 1
    },
    hourStep: {
      type: Number,
      default: 1
    },
    minuteStep: {
      type: Number,
      default: 1
    },
    secondStep: {
      type: Number,
      default: 1
    },
    limitCurMonth: {
      type: Boolean,
      default: false
    },
    suffixFlag: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      result: [],
      time: {
        year: 0,
        month: 0,
        date: 0,
        hour: 0,
        minute: 0,
        second: 0
      },
      baseMonth: 12,
      baseDate: 31,
      baseHour: 23,
      baseMinute: 59,
      baseSecond: 59,
      yearRange: [],
      monthRange: [],
      dateRange: [],
      hourRange: [],
      minuteRange: [],
      secondRange: [],
      maxTimeObj: null,
      minTimeObj: null,
      startTime: null,
      endTime: null,
      activeTime: 'startTime',
      typeList: [
        {text: '按日选择', value: 'date'},
        {text: '按月选择', value: 'month'},
        {text: '按年选择', value: 'year'}
      ]
    };
  },
  watch: {
    showFlag: function (value) {
      this.initTime();
    },
    time: {
      handler: function (val, oldVal) {
        let {year, month = 1, date = 1, hour = 0, minute = 0, second = 0} = val;
        this[this.activeTime] = new Date(year, month - 1, date, hour, minute, second);
        if (this.range && this.startTime > this.endTime) {
          if (this.activeTime === 'startTime') {
            this.endTime = this.startTime;
          } else {
            this.startTime = this.endTime;
          }
        } else if (this.limitCurMonth) {
          let sameYear = this.startTime.getFullYear() === this.endTime.getFullYear();
          let sameMonth = this.startTime.getMonth() === this.endTime.getMonth();
          if (!sameYear || !sameMonth) {
            this.endTime = this.startTime;
          }
        }
      },
      deep: true
    },
    'time.year': function (val, oldVal) {
      let condition = this.resetCondition('year');
      condition = oldVal === this.baseTime.maxYear || oldVal === this.baseTime.minYear || condition;
      if (condition || this.baseTime.curMonth === 2) {
        if (condition) this.setMonth();
        this.setDate();
        if (condition) {
          this.setHour();
          this.setMinute();
          this.setSecond();
        }
      }
    },
    'time.month': function () {
      this.setDate();
      if (this.resetCondition('month')) {
        this.setHour();
        this.setMinute();
        this.setSecond();
      }
    },
    'time.date': function () {
      if (this.resetCondition('date')) {
        this.setHour();
        this.setMinute();
        this.setSecond();
      }
    },
    'time.hour': function () {
      if (this.resetCondition('hour')) {
        this.setMinute();
        this.setSecond();
      }
    },
    'time.minute': function () {
      if (this.resetCondition('minute')) {
        this.setSecond();
      }
    },
    type: function () {
      if (!this.initFlag) return;
      if (!this.monthRange.length) {
        this.setMonth();
      }
      if (!this.dateRange.length) {
        this.setDate();
      }
    },
    range: function () {
      if (!this.initFlag) return;
      if (this.range && !this.endTime) {
        this.endTime = this.startTime;
      }
    }
  },
  computed: {
    dataList() {
      return [this.yearRange, this.minuteRange, this.dateRange, this.hourRange, this.minuteRange, this.secondRange];
    },
    defaultFormat() {
      if (this.format) return this.format;
      switch (this.type) {
        case 'date':
          return 'yyyy-MM-dd';
        case 'year-month':
          return 'yyyy-MM';
        case 'month-date':
          return 'MM-dd';
        case 'datetime':
          return 'yyyy-MM-dd hh:mm:ss';
        case 'time':
          return 'hh:mm:ss';
        case 'hour-minute':
          return 'hh:mm';
        case 'minute-second':
          return 'mm:ss';
        default:
          return 'yyyy-MM-dd';
      }
    },
    currentTime() {
      return this[this.activeTime];
    },
    baseTime() {
      let minTime = this.minTimeObj;
      let maxTime = this.maxTimeObj;
      let currentTime = this.time;
      return {
        minYear: minTime.getFullYear(),
        maxYear: maxTime.getFullYear(),
        curYear: currentTime.year,
        minMonth: minTime.getMonth() + 1,
        maxMonth: maxTime.getMonth() + 1,
        curMonth: currentTime.month,
        minDate: minTime.getDate(),
        maxDate: maxTime.getDate(),
        curDate: currentTime.date,
        minHour: minTime.getHours(),
        maxHour: maxTime.getHours(),
        curHour: currentTime.hour,
        minMinute: minTime.getMinutes(),
        maxMinute: maxTime.getMinutes(),
        curMinute: currentTime.minute,
        minSecond: minTime.getSeconds(),
        maxSecond: maxTime.getSeconds(),
        curSecond: currentTime.second
      };
    },
    isRequire() {
      let year, month, date, hour, minute, second;
      switch (this.type) {
        case 'date':
          year = month = date = true;
          break;
        case 'year-month':
          year = month = true;
          break;
        case 'month-date':
          month = date = true;
          break;
        case 'datetime':
          year = month = date = hour = minute = second = true;
          break;
        case 'time':
          hour = minute = second = true;
          break;
        case 'hour-minute':
          hour = minute = true;
          break;
        case 'minute-second':
          minute = second = true;
          break;
      }
      return {
        year, month, date, hour, minute, second
      };
    },
    isLeapYear() {
      let curYear = this.baseTime.curYear;
      return (curYear % 4 === 0 && curYear % 100 !== 0) || curYear % 400 === 0;
    },
    compare() {
      let {
        minYear,
        maxYear,
        curYear,
        minMonth,
        maxMonth,
        curMonth,
        minDate,
        maxDate,
        curDate,
        minHour,
        maxHour,
        curHour,
        minMinute,
        maxMinute,
        curMinute
      } = this.baseTime;
      let sameMinYear = minYear === curYear;
      let sameMaxYear = maxYear === curYear;
      let sameMinMonth = minMonth === curMonth && sameMinYear;
      let sameMaxMonth = maxMonth === curMonth && sameMaxYear;
      let sameMinDate = minDate === curDate && sameMinMonth;
      let sameMaxDate = maxDate === curDate && sameMaxMonth;
      let sameMinHour = minHour === curHour && sameMinDate;
      let sameMaxHour = maxHour === curHour && sameMaxDate;
      let sameMinMinute = minMinute === curMinute && sameMinHour;
      let sameMaxMinute = maxMinute === curMinute && sameMaxHour;
      return {
        sameMinYear,
        sameMaxYear,
        sameMinMonth,
        sameMaxMonth,
        sameMinDate,
        sameMaxDate,
        sameMinHour,
        sameMaxHour,
        sameMinMinute,
        sameMaxMinute,
        sameYear: sameMinYear && sameMaxYear,
        sameMonth: sameMinMonth && sameMaxMonth,
        sameDate: sameMinDate && sameMaxDate,
        sameHour: sameMinHour && sameMaxHour,
        sameMinute: sameMinMinute && sameMaxMinute
      };
    }
  },
  methods: {
    handleSubmit() {
      let startTime = this.formatDate(this.startTime);
      if (this.range) {
        let endTime = this.formatDate(this.endTime);
        let result = [startTime, endTime];
        this.$emit('input', result);
        this.$emit('submit', result);
      } else {
        this.$emit('input', startTime);
        this.$emit('submit', startTime);
      }
      this.hidePopup();
    },
    hidePopup() {
      this.$emit('update:showFlag', false);
    },
    getDateObj(value) {
      if (Validate.isDate(value)) {
        return value;
      } else if (Validate.isString(value) && value !== '') {
        let arr = value.split(/[-/ :]/);
        let d = new Date();
        let year = d.getFullYear();
        let month = d.getMonth();
        let date = d.getDate();
        let hour = d.getHours();
        switch (this.type) {
          case 'date':
            return new Date(arr[0], arr[1] - 1, arr[2]);
          case 'year-month':
            return new Date(arr[0], arr[1] - 1, 1);
          case 'month-date':
            return new Date(year, arr[0] - 1, arr[1]);
          case 'datetime':
            return new Date(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]);
          case 'time':
            return new Date(year, month, date, arr[0], arr[1], arr[2]);
          case 'hour-minute':
            return new Date(year, month, date, arr[0], arr[1], 0);
          case 'minute-second':
            return new Date(year, month, date, hour, arr[0], arr[1]);
        }
      } else {
        return new Date();
      }
    },
    resetCondition(time) {
      let key = toCapitalize(time);
      if (this.compare['sameMin' + key] || this.compare['sameMax' + key] || this[time + 'Range'].length < this['base' + key]) {
        return true;
      }
    },
    setCurrentTime(activeTime) {
      this.time.year = activeTime.getFullYear();
      this.time.month = activeTime.getMonth() + 1;
      this.time.date = activeTime.getDate();
      this.time.hour = activeTime.getHours();
      this.time.minute = activeTime.getMinutes();
      this.time.second = activeTime.getSeconds();
    },
    handleChangeActiveTime(activeTime) {
      this.activeTime = activeTime;
      this.setCurrentTime(this[activeTime]);
      if (this.limitCurMonth) {
        this.setYear();
        this.setMonth();
        this.$nextTick(() => {
          this.$refs.year.setScrollTop();
          this.$refs.month.setScrollTop();
        });
      }
    },
    initTime() {
      if (!this.showFlag) return;
      if (this.range) {
        if (!(Array.isArray(this.value) || this.value === '')) {
          throw Error('Value must be an array or empty string when props[range] is true.');
        }
        this.startTime = this.getDateObj(this.value[0]);
        this.endTime = this.getDateObj(this.value[1]);
      } else {
        this.startTime = this.getDateObj(this.value);
      }
      this.minTimeObj = this.getDateObj(this.minTime);
      this.maxTimeObj = this.getDateObj(this.maxTime);
      this.setYear();
      this.setCurrentTime(this.currentTime);
      this.setMonth();
      this.setDate();
      this.setHour();
      this.setMinute();
      this.setSecond();
    },
    generateTimeRange(minVal, maxVal, step = 1) {
      let list = [];
      for (let i = minVal; i <= maxVal; i += step) {
        list.push(i);
      }
      return list;
    },
    setTimeRange(time, preTime, start = 1) {
      if (!this.isRequire[time]) return;
      let key = toCapitalize(time);
      let preKey = toCapitalize(preTime);
      let min = this.baseTime['min' + key];
      let max = this.baseTime['max' + key];
      let result = [];
      switch (true) {
        case this.compare['same' + preKey]:
          result = this.generateTimeRange(min, max, this[time + 'Step']);
          break;
        case this.compare['sameMin' + preKey]:
          result = this.generateTimeRange(min, this['base' + key], this[time + 'Step']);
          break;
        case this.compare['sameMax' + preKey]:
          result = this.generateTimeRange(start, max, this[time + 'Step']);
          break;
        default:
          result = this.generateTimeRange(start, this['base' + key], this[time + 'Step']);
      }
      this[time + 'Range'] = result;
    },
    setYear() {
      if (this.limitCurMonth && this.activeTime === 'endTime') {
        return this.yearRange = [this.baseTime.curYear];
      }
      this.yearRange = this.generateTimeRange(this.minTimeObj.getFullYear(), this.maxTimeObj.getFullYear(), this.yearStep);
    },
    setMonth() {
      if (this.limitCurMonth && this.activeTime === 'endTime') {
        return this.monthRange = [this.baseTime.curMonth];
      }
      this.setTimeRange('month', 'year');
    },
    setDate() {
      if (!this.isRequire.date) return;
      let {curMonth} = this.baseTime;
      if (curMonth === 2) {
        if (this.isLeapYear) {
          this.baseDate = 29;
        } else {
          this.baseDate = 28;
        }
      } else if ((curMonth <= 7 && curMonth % 2 !== 0) || (curMonth > 7 && curMonth % 2 === 0)) {
        this.baseDate = 31;
      } else {
        this.baseDate = 30;
      }
      this.setTimeRange('date', 'month');
    },
    setHour() {
      this.setTimeRange('hour', 'date', 0);
    },
    setMinute() {
      this.setTimeRange('minute', 'hour', 0);
    },
    setSecond() {
      this.setTimeRange('second', 'minute', 0);
    },
    formatDate(date) {
      if (!date) return '';
      return date.Format(this.defaultFormat);
    },
    getSuffix(suffix) {
      return this.suffixFlag ? suffix : '';
    }
  },
  mounted() {
    this.initTime();
  }
};
</script>

<style lang="scss" scoped>
.date-picker {
  .opt-box {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 0.4rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
    padding: 0 0.1rem;
  }

  .current-time {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0.15rem 0 0.05rem;

    .current-time-item {
      color: $gray;
      border-bottom: 1px solid $gray;
      height: 0.45rem;
      line-height: 0.45rem;
      padding: 0 0.05rem;
      font-size: 0.15rem;
      margin: 0 0.1rem;
    }

    .active {
      color: $primary;
      border-bottom: 1px solid $primary;
    }
  }

  /deep/ .popup.bottom {
    padding: 0;

    .popup-title {
      display: none;
    }
  }
}
</style>
